﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;

using alib;
using alib.Collections.ReadOnly;
using alib.Enumerable;
using alib.IO;
using alib.Reflection;

namespace agree.itsdb
{
	using Type = System.Type;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{Name,nq}")]
	public class TsdbDatabase : IdentList<TsdbTable>, ICorpus, ISupportInitializeNotification, schema.ρρName
	{
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public static readonly Type[] ttypes = 
		{
			typeof(table.Item),
			typeof(table.Analysis),
			typeof(table.Phenomenon),
			typeof(table.Parameter),
			typeof(table.Set),
			typeof(table.Item_Phenomenon),
			typeof(table.Item_Set),
			typeof(table.Run),
			typeof(table.Parse),
			typeof(table.Result),
			typeof(table.Rule),
			typeof(table.Output),
			typeof(table.Edge),
			typeof(table.Tree),
			typeof(table.Decision),
			typeof(table.Preference),
			typeof(table.Update),
			typeof(table.Fold),
			typeof(table.Score),
		};

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Dictionary<String, TsdbTable> table_name_lookup = new Dictionary<String, TsdbTable>(StringComparer.OrdinalIgnoreCase);

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IList<TsdbTable> Tables { get { return this; } }

		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		public TsdbTable[] _dbg_display { get { return this.ToArray(); } }

		new public IReadOnlyList<IString> Items { get { return ItemTable; } }

		//[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		//public String Name { get { return Path.GetFileName(s_dir); } }
		//[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		//public String Description { get { return String.Format("ERG/Redwoods corpus '{0}'", Name); } }
		//[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		//public String IsoLanguage { get { return iso_language; } }


		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public table.Item ItemTable { get { return (table.Item)table_name_lookup["item"]; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public table.Analysis AnalysisTable { get { return (table.Analysis)table_name_lookup["analysis"]; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public table.Parse ParseTable { get { return (table.Parse)table_name_lookup["parse"]; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public table.Result ResultsTable { get { return (table.Result)table_name_lookup["result"]; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public table.Run RunTable { get { return (table.Run)table_name_lookup["run"]; } }

		//[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		//public String SourceDirectory { get { return LoadDirectory; } }
		//[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		//readonly String s_dir;
		//[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		//readonly String iso_language;

		public String Name { get; set; }

		public String LoadDirectory { get; set; }

		public bool Uncompress { get; set; }

		public String DatabaseName { get { return Path.GetFileName(LoadDirectory); } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public TsdbTable TableFromAnonymousItems(String name, String description, IEnumerable data)
		{
			Object prototype = data.OfType<Object>().First();

			var pi = AnonymousTypePromoter.GetPromotionInfo(
						name,
						prototype,
						new[] { "System.ComponentModel", "agree.itsdb" },
						new[] { "System.dll", Assembly.GetAssembly(typeof(entity)).Location },
						new[] { typeof(entity) });

			Type T_tab = typeof(TsdbTable<>).MakeGenericType(new[] { pi.Type });

			TsdbTable t = (TsdbTable)Activator.CreateInstance(T_tab, this, name, description);

			foreach (Object o in data)
				t.Add(pi.Promote(o));

			this.Add(t);
			return t;
		}

		public TsdbDatabase(IIdentity prv)
			: base(prv)
		{
		}


		public TsdbDatabase(String load_dir, bool f_uncompress, IIdentity prv = default(IIdentity))
			: base(prv)
		{
			LoadDirectory = load_dir;
			Uncompress = f_uncompress;
			Start();
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Construct an Itsdb database over the specified directory
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public void Start()
		{
			//this.iso_language = iso_language;
			this.Name = Path.GetFileNameWithoutExtension(LoadDirectory);

			Object[] ctor_args = new Object[] { this };
			for (int i = 0; i < ttypes.Length; i++)
			{
				var x = Activator.CreateInstance(ttypes[i], ctor_args);
				TsdbTable t = (TsdbTable)x;
				t.Name = ttypes[i].Name.ToLower().Replace('-', '_');
				this.Add(t);
				table_name_lookup.Add(t.Name, t);
			}

			String relations_file = Path.Combine(LoadDirectory, "relations");
			if (!File.Exists(relations_file))
			{
				String msg = String.Format("The filename 'relations' could not be found in the directory '{0}'", LoadDirectory);
				throw new FileNotFoundException(msg, "relations");
			}

			foreach (var schema_data in File.ReadAllLines(relations_file)
				.Select(l =>
				{
					int ix = l.IndexOf('#');
					if (ix != -1)
						l = l.Remove(ix);
					return l.Trim();
				})
				.Partition(l => l == String.Empty)
				.Select(part =>
				{
					String s_tab = part[0].TrimEnd(':');
					part.RemoveAt(0);
					for (int i = 0; i < part.Count; i++)
						part[i] = part[i].Split(default(Char[]), StringSplitOptions.RemoveEmptyEntries)[0];
					return new { s_tab, Fields = part };
				}))
			{
				TsdbTable cur;
				if (!table_name_lookup.TryGetValue(schema_data.s_tab.Replace('-', '_'), out cur))
				{
					String msg = String.Format("The relations file refers to an unknown table type '{0}'", schema_data.s_tab);
					throw new Exception(msg);
				}
				Type T_sch = typeof(Schema<>).MakeGenericType(new[] { cur.ItsdbType });
				Schema sch = (Schema)Activator.CreateInstance(T_sch, new object[] { schema_data.Fields });
				cur.SetPathAndSchema(LoadDirectory, sch);
			}
		}

		//public void Load()
		//{
		//	int c;
		//	foreach (var tab in this)
		//		c = tab.Count;
		//}

		public TsdbTable GetTableFromItemType(Type t)
		{
			return Tables.First(tt => tt.ItsdbType == t);
		}

		public event EventHandler Initialized;

		public bool IsInitialized
		{
			get { return true; }
		}

		public void BeginInit()
		{
		}

		public void EndInit()
		{
			Start();
		}

		IReadOnlyList<IString> ICorpus.Items
		{
			get { throw not.impl; }
		}

		IEnumerator<IString> IEnumerable<IString>.GetEnumerator()
		{
			throw not.impl;
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			throw not.impl;
		}

		IString IReadOnlyList<IString>.this[int index]
		{
			get { throw not.impl; }
		}
	};
}
